﻿using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using ZFramework.Runtime;

namespace ZFramework.Editor
{
    [CustomEditor(typeof(GameWorldManager))]
    internal sealed class GameWorldManagerInspecotr : ZFrameworkInspector
    {
        private SerializedProperty m_AvailableGameWorldTypeNames = null;
        private SerializedProperty m_EntranceGameWorldTypeName = null;

        private string[] m_GameWorldTypeNames = null;
        private List<string> m_CurrentAvailableGameWorldTypeNames = null;
        private int m_EntranceGameWorldIndex = -1;

        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            serializedObject.Update();

            GameWorldManager t = (GameWorldManager)target;

            if (string.IsNullOrEmpty(m_EntranceGameWorldTypeName.stringValue))
            {
                EditorGUILayout.HelpBox("Entrance gameWorld is invalid.", MessageType.Error);
            }
            else if (EditorApplication.isPlaying)
            {
                EditorGUILayout.LabelField("Current GameWorld", t.CurrentGameWorld == null ? "None" : t.CurrentGameWorld.GetType().ToString());
            }

            EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode);
            {
                GUILayout.Label("Available GameWorlds", EditorStyles.boldLabel);
                if (m_GameWorldTypeNames.Length > 0)
                {
                    EditorGUILayout.BeginVertical("box");
                    {
                        foreach (string gameWorldTypeName in m_GameWorldTypeNames)
                        {
                            bool selected = m_CurrentAvailableGameWorldTypeNames.Contains(gameWorldTypeName);
                            if (selected != EditorGUILayout.ToggleLeft(gameWorldTypeName, selected))
                            {
                                if (!selected)
                                {
                                    m_CurrentAvailableGameWorldTypeNames.Add(gameWorldTypeName);
                                    WriteAvailableGameWorldTypeNames();
                                }
                                else if (gameWorldTypeName != m_EntranceGameWorldTypeName.stringValue)
                                {
                                    m_CurrentAvailableGameWorldTypeNames.Remove(gameWorldTypeName);
                                    WriteAvailableGameWorldTypeNames();
                                }
                            }
                        }
                    }
                    EditorGUILayout.EndVertical();
                }
                else
                {
                    EditorGUILayout.HelpBox("There is no available gameWorld.", MessageType.Warning);
                }

                if (m_CurrentAvailableGameWorldTypeNames.Count > 0)
                {
                    EditorGUILayout.Separator();

                    int selectedIndex = EditorGUILayout.Popup("Entrance GameWorld", m_EntranceGameWorldIndex, m_CurrentAvailableGameWorldTypeNames.ToArray());
                    if (selectedIndex != m_EntranceGameWorldIndex)
                    {
                        m_EntranceGameWorldIndex = selectedIndex;
                        m_EntranceGameWorldTypeName.stringValue = m_CurrentAvailableGameWorldTypeNames[selectedIndex];
                    }
                }
                else
                {
                    EditorGUILayout.HelpBox("Select available gameWorlds first.", MessageType.Info);
                }
            }
            EditorGUI.EndDisabledGroup();

            serializedObject.ApplyModifiedProperties();

            Repaint();
        }

        protected override void OnCompileComplete()
        {
            base.OnCompileComplete();

            RefreshTypeNames();
        }

        private void OnEnable()
        {
            m_AvailableGameWorldTypeNames = serializedObject.FindProperty("m_AvailableGameWorldTypeNames");
            m_EntranceGameWorldTypeName = serializedObject.FindProperty("m_EntranceGameWorldTypeName");

            RefreshTypeNames();
        }

        private void RefreshTypeNames()
        {
            m_GameWorldTypeNames = Type.GetTypeNames(typeof(GameWorldBase));
            ReadAvailableGameWorldTypeNames();
            int oldCount = m_CurrentAvailableGameWorldTypeNames.Count;
            m_CurrentAvailableGameWorldTypeNames = m_CurrentAvailableGameWorldTypeNames.Where(x => m_GameWorldTypeNames.Contains(x)).ToList();
            if (m_CurrentAvailableGameWorldTypeNames.Count != oldCount)
            {
                WriteAvailableGameWorldTypeNames();
            }
            else if (!string.IsNullOrEmpty(m_EntranceGameWorldTypeName.stringValue))
            {
                m_EntranceGameWorldIndex = m_CurrentAvailableGameWorldTypeNames.IndexOf(m_EntranceGameWorldTypeName.stringValue);
                if (m_EntranceGameWorldIndex < 0)
                {
                    m_EntranceGameWorldTypeName.stringValue = null;
                }
            }

            serializedObject.ApplyModifiedProperties();
        }

        private void ReadAvailableGameWorldTypeNames()
        {
            m_CurrentAvailableGameWorldTypeNames = new List<string>();
            int count = m_AvailableGameWorldTypeNames.arraySize;
            for (int i = 0; i < count; i++)
            {
                m_CurrentAvailableGameWorldTypeNames.Add(m_AvailableGameWorldTypeNames.GetArrayElementAtIndex(i).stringValue);
            }
        }

        private void WriteAvailableGameWorldTypeNames()
        {
            m_AvailableGameWorldTypeNames.ClearArray();
            if (m_CurrentAvailableGameWorldTypeNames == null)
            {
                return;
            }

            m_CurrentAvailableGameWorldTypeNames.Sort();
            int count = m_CurrentAvailableGameWorldTypeNames.Count;
            for (int i = 0; i < count; i++)
            {
                m_AvailableGameWorldTypeNames.InsertArrayElementAtIndex(i);
                m_AvailableGameWorldTypeNames.GetArrayElementAtIndex(i).stringValue = m_CurrentAvailableGameWorldTypeNames[i];
            }

            if (!string.IsNullOrEmpty(m_EntranceGameWorldTypeName.stringValue))
            {
                m_EntranceGameWorldIndex = m_CurrentAvailableGameWorldTypeNames.IndexOf(m_EntranceGameWorldTypeName.stringValue);
                if (m_EntranceGameWorldIndex < 0)
                {
                    m_EntranceGameWorldTypeName.stringValue = null;
                }
            }
        }
    }
}